// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/function/builtins/add.h>
#include <cantor/function/scalar_function.h>
#include <turbo/numeric/float16.h>
#include <cstdint>

namespace cantor::builtin {
    template<typename T>
    struct Add2Impl {
        T operator()(T t1, T t2) {
            return t1 + t2;
        }
    };

    static turbo::Status initialize_add2(ScalarFunctionBuilder &builder) {
        std::vector<std::string> names = {"a", "b"};
        std::string description = "add for two";
        /// int8_t
        auto rs = builder.register_binary_scalar_function<int8_t, int8_t, int8_t, Add2Impl<int8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int16_t
        rs = builder.register_binary_scalar_function<int16_t, int16_t, int16_t, Add2Impl<int16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int32_t
        rs = builder.register_binary_scalar_function<int32_t, int32_t, int32_t, Add2Impl<int32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int64_t
        rs = builder.register_binary_scalar_function<int64_t, int64_t, int64_t, Add2Impl<int64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::int128
        rs = builder.register_binary_scalar_function<turbo::int128, turbo::int128, turbo::int128, Add2Impl<
            turbo::int128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint8_t
        rs = builder.register_binary_scalar_function<uint8_t, uint8_t, uint8_t, Add2Impl<uint8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// uint16_t
        rs = builder.register_binary_scalar_function<uint16_t, uint16_t, uint16_t, Add2Impl<uint16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint32_t
        rs = builder.register_binary_scalar_function<uint32_t, uint32_t, uint32_t, Add2Impl<uint32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint64_t
        rs = builder.register_binary_scalar_function<uint64_t, uint64_t, uint64_t, Add2Impl<uint64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::uint128
        rs = builder.register_binary_scalar_function<turbo::uint128, turbo::uint128, turbo::uint128, Add2Impl<
            turbo::uint128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// float
        rs = builder.register_binary_scalar_function<float, float, float, Add2Impl<float> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// double
        rs = builder.register_binary_scalar_function<double, double, double, Add2Impl<double> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        return turbo::OkStatus();
    }

    template<typename T>
    struct Add3Impl {
        T operator()(T t1, T t2, T t3) {
            return t1 + t2 + t3;
        }
    };

    static turbo::Status initialize_add3(ScalarFunctionBuilder &builder) {
        std::vector<std::string> names = {"a", "b", "c"};
        std::string description = "add for three";
        /// int8_t
        auto rs = builder.register_ternary_scalar_function<int8_t, int8_t, int8_t, int8_t, Add3Impl<int8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int16_t
        rs = builder.register_ternary_scalar_function<int16_t, int16_t, int16_t, int16_t, Add3Impl<int16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int32_t
        rs = builder.register_ternary_scalar_function<int32_t, int32_t, int32_t, int32_t, Add3Impl<int32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int64_t
        rs = builder.register_ternary_scalar_function<int64_t, int64_t, int64_t, int64_t, Add3Impl<int64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::int128
        rs = builder.register_ternary_scalar_function<turbo::int128, turbo::int128, turbo::int128, turbo::int128,
            Add3Impl<turbo::int128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint8_t
        rs = builder.register_ternary_scalar_function<uint8_t, uint8_t, uint8_t, uint8_t, Add3Impl<uint8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// uint16_t
        rs = builder.register_ternary_scalar_function<uint16_t, uint16_t, uint16_t, uint16_t, Add3Impl<uint16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint32_t
        rs = builder.register_ternary_scalar_function<uint32_t, uint32_t, uint32_t, uint32_t, Add3Impl<uint32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint64_t
        rs = builder.register_ternary_scalar_function<uint64_t, uint64_t, uint64_t, uint64_t, Add3Impl<uint64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::uint128
        rs = builder.register_ternary_scalar_function<turbo::uint128, turbo::uint128, turbo::uint128, turbo::uint128,
            Add3Impl<turbo::uint128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// float
        rs = builder.register_ternary_scalar_function<float, float, float, float, Add3Impl<float> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// double
        rs = builder.register_ternary_scalar_function<double, double, double, double, Add3Impl<double> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        return turbo::OkStatus();
    }

    template<typename T>
    struct Add4Impl {
        T operator()(T t1, T t2, T t3, T t4) {
            return t1 + t2 + t3 + t4;
        }
    };

    static turbo::Status initialize_add4(ScalarFunctionBuilder &builder) {
        std::vector<std::string> names = {"a", "b", "c", "d"};
        std::string description = "add for four";
        /// int8_t
        auto rs = builder.register_quaternary_scalar_function<int8_t, int8_t, int8_t, int8_t, int8_t, Add4Impl<
            int8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int16_t
        rs = builder.register_quaternary_scalar_function<int16_t, int16_t, int16_t, int16_t, int16_t, Add4Impl<
            int16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int32_t
        rs = builder.register_quaternary_scalar_function<int32_t, int32_t, int32_t, int32_t, int32_t, Add4Impl<
            int32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int64_t
        rs = builder.register_quaternary_scalar_function<int64_t, int64_t, int64_t, int64_t, int64_t, Add4Impl<
            int64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::int128
        rs = builder.register_quaternary_scalar_function<turbo::int128, turbo::int128, turbo::int128, turbo::int128,
            turbo::int128,
            Add4Impl<turbo::int128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint8_t
        rs = builder.register_quaternary_scalar_function<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, Add4Impl<
            uint8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// uint16_t
        rs = builder.register_quaternary_scalar_function<uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, Add4Impl<
            uint16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint32_t
        rs = builder.register_quaternary_scalar_function<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, Add4Impl<
            uint32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint64_t
        rs = builder.register_quaternary_scalar_function<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, Add4Impl<
            uint64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::uint128
        rs = builder.register_quaternary_scalar_function<turbo::uint128, turbo::uint128, turbo::uint128, turbo::uint128,
            turbo::uint128,
            Add4Impl<turbo::uint128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// float
        rs = builder.register_quaternary_scalar_function<float, float, float, float, float, Add4Impl<float> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// double
        rs = builder.register_quaternary_scalar_function<double, double, double, double, double, Add4Impl<double> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        return turbo::OkStatus();
    }

    template<typename T>
    struct Add5Impl {
        T operator()(T t1, T t2, T t3, T t4, T t5) {
            return t1 + t2 + t3 + t4 + t5;
        }
    };

    static turbo::Status initialize_add5(ScalarFunctionBuilder &builder) {
        std::vector<std::string> names = {"a", "b", "c", "d", "e"};
        std::string description = "add for five";
        /// int8_t
        auto rs = builder.register_quinary_scalar_function<int8_t, int8_t, int8_t, int8_t, int8_t, int8_t, Add5Impl<
            int8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int16_t
        rs = builder.register_quinary_scalar_function<int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, Add5Impl<
            int16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int32_t
        rs = builder.register_quinary_scalar_function<int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, Add5Impl<
            int32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// int64_t
        rs = builder.register_quinary_scalar_function<int64_t, int64_t, int64_t, int64_t, int64_t, int64_t, Add5Impl<
            int64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::int128
        rs = builder.register_quinary_scalar_function<turbo::int128, turbo::int128, turbo::int128, turbo::int128,
            turbo::int128,
            turbo::int128,
            Add5Impl<turbo::int128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint8_t
        rs = builder.register_quinary_scalar_function<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, Add5Impl<
            uint8_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// uint16_t
        rs = builder.register_quinary_scalar_function<uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t,
            Add5Impl<
                uint16_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint32_t
        rs = builder.register_quinary_scalar_function<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
            Add5Impl<
                uint32_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// uint64_t
        rs = builder.register_quinary_scalar_function<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t,
            Add5Impl<
                uint64_t> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);
        /// turbo::uint128
        rs = builder.register_quinary_scalar_function<turbo::uint128, turbo::uint128, turbo::uint128, turbo::uint128,
            turbo::uint128,
            turbo::uint128,
            Add5Impl<turbo::uint128> >("add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// float
        rs = builder.register_quinary_scalar_function<float, float, float, float, float, float, Add5Impl<float> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        /// double
        rs = builder.register_quinary_scalar_function<double, double, double, double, double, double, Add5Impl<
            double> >(
            "add", names, description);
        TURBO_RETURN_NOT_OK(rs);

        return turbo::OkStatus();
    }

    turbo::Status initialize_add(FunctionRegistry *registry) {
        ScalarFunctionBuilder builder(registry);
        TURBO_RETURN_NOT_OK(initialize_add2(builder));
        TURBO_RETURN_NOT_OK(initialize_add3(builder));
        TURBO_RETURN_NOT_OK(initialize_add4(builder));
        TURBO_RETURN_NOT_OK(initialize_add5(builder));
        return turbo::OkStatus();
    }
} // namespace cantor::builtin
